%{ open Ast %}


%token PLUS MINUS TIMES DIVIDE ASSIGN MODULE
%token GEQ LEQ GT LT EQ NEQ EQTYPE NEQTYPE
%token NOT AND OR
%token CONCAT
%token ADDLTR ADDRTL REMOVERTL REMOVELTR
%token SEMI LPAREN RPAREN LBRACE RBRACE COMMA
%token ANYTYPE BOOL CARD DEF DOUBLE ELE ELSE FALSE FOREACH IF INT LIST PLAYER RETURN STRING TRUE WHILE YOUR
%token <int> LITERAL
%token <string> ID
%token EOF

%nonassoc NOELSE
%nonassoc ELE
%nonassoc ELSE

%right ASSIGN

%left ADDR		        (* List Operators *)		
%left REMOVER
%left ADDL
%left REMOVEL

%left NOT AND OR 
%left EQ NEQ
%left LT GT LEQ GEQ
%left PLUS MINUS
%left TIMES DIVIDE

%start program
%type <Ast.program> program

%%

program:
   /* nothing */ { [], [] }
 | program vdecl { ($2 :: fst $1), snd $1 }
 | program fdecl { fst $1, ($2 :: snd $1) }

fdecl:
   DEF ID LPAREN formals_opt RPAREN LBRACE vdecl_list stmt_list RBRACE
     { { fname = $2;
	       formals = $4;
	       locals = List.rev $7;
	       body   = List.rev $8 
		 } }

formals_opt:
    /* nothing */ { [] }
  | formal_list   { List.rev $1 }

formal_list:
    ID                   { [$1] }
  | formal_list COMMA ID { $3 :: $1 }

vdecl_list:
    /* nothing */    { [] }
  | vdecl_list vdecl { $2 :: $1 }

vdecl:
   INT ID SEMI { $2 }

stmt_list:
    /* Empty blocks not allowed */  { [] }
  | stmt_list stmt { $2 :: $1 }

stmt:
    expr SEMI { Expr($1) }
  | RETURN expr SEMI { Return($2) }
  | LBRACE stmt_list RBRACE { Block(List.rev $2) }
  | IF LPAREN expr RPAREN stmt %prec NOELSE { If($3, $5, Block([])) }
  | IF LPAREN expr RPAREN stmt ELSE stmt    { If($3, $5, $7) }
  | FOREACH LPAREN expr_opt SEMI expr_opt SEMI expr_opt RPAREN stmt
     { For($3, $5, $7, $9) }
  | WHILE LPAREN expr RPAREN stmt { While($3, $5) }

expr_opt:
    /* nothing */ { Noexpr }
  | expr          { $1 }

expr:
    LITERAL          { Literal($1) }
	| 	
  | DOUBLE           { Double ($1) }
	| ID               { Id($1) }
  | expr PLUS   expr { Binop($1, Add,   $3) }
  | expr MINUS  expr { Binop($1, Sub,   $3) }
  | expr TIMES  expr { Binop($1, Mult,  $3) }
  | expr DIVIDE expr { Binop($1, Div,   $3) }
	| TRUE             { Bval (True)    } 
	| FALSE						 { Bval (False)   }
  | expr EQ  expr    { Binop($1, Equal, $3) }
  | expr NEQ expr    { Binop($1, Neq,   $3) }
  | expr GT  expr    { Binop($1, Greater,  $3) }
  | expr GEQ expr    { Binop($1, Geq,   $3) }
  | expr LT  expr    { Binop($1, Less,  $3) }
  | expr LEQ expr    { Binop($1, Leq,   $3) }
	| expr AND expr    { BExpr($1, And, $3) }
	| expr OR  expr    { Bexpr($1, Or, $3)  }
  | ID ASSIGN expr   { Assign($1, $3) }
  | ID LPAREN actuals_opt RPAREN { Call($1, $3) }
  | LPAREN expr RPAREN { $2 }

actuals_opt:
    /* nothing */ { [] }
  | actuals_list  { List.rev $1 }

actuals_list:
    expr                    { [$1] }
  | actuals_list COMMA expr { $3 :: $1 }
